
var ContinueSentinel = {};

class Context {
  done =  false;
  method =  "next";
  next = 0;
  prev =  0;
  abrupt(type, arg) {
    var record = {};
    record.type = type;
    record.arg = arg;
    return this.complete(record);
  }
  complete(record, afterLoc) {
    if (record.type === "return") {
      this.rval = this.arg = record.arg;
      this.method = "return";
      this.next = "end";
    }

    return ContinueSentinel;
  }
  stop() {
    this.done = true;
    return this.rval;
  }
};

let regeneratorRuntime = {
   wrap(innerFn, outerFn) {
      defineIteratorMethods(outerFn.prototype);
      var generator = Object.create(outerFn.prototype);
      var context = new Context();
      generator._invoke = makeInvokeMethod(innerFn, context);
      return generator;
   },
   mark(genFun) {
      return genFun;
   }
}

function defineIteratorMethods(prototype) {
  ["next", "throw", "return"].forEach(function(method) {
    prototype[method] = function(arg) {
      return this._invoke(method, arg);
    };
  });
}

function makeInvokeMethod(innerFn, context) {
  // 状态设置为start
  var state = 'start';
  return function invoke(method, arg) {
  // 已完成
    if (state === 'completed') {
      return { value: undefined, done: true };
    }
    context.method = method;
    context.arg = arg;
    // 执行中
    while (true) {
      state = 'executing';
      var record = {
        type: 'normal',
        arg: innerFn.call(this, context) // 执行下一步，并获取状态（其实就是switch里return的值）
      };
      if (record.type === "normal") {
      // 判断是否已经执行完成
        state = context.done
          ? 'completed'
          : 'yield';
 		  // 	ContinueSentinel其实是一个空对象，record.arg === {}则跳过return进入下一个循环，那什么什么record.arg会为空对象呢，答案是没有后续yield语句或已经return 的时候，也就是switch反悔了空值的情况
        if (record.arg === ContinueSentinel) {
          continue;
        }
        return {
          value: record.arg,
          done: context.done
        };

      }
    }
  };
}

let _marked = regeneratorRuntime.mark(helloWorldGenerator)

function helloWorldGenerator() {
  return regeneratorRuntime.wrap(
    function helloWorldGenerator$(_context) {
      while (1) {
        switch ((_context.prev = _context.next)) {
          case 0:
            _context.next = 2;
            return "hello";
 
          case 2:
            _context.next = 4;
            return "world";
 
          case 4:
            return _context.abrupt("return", "ending");
 
          case 5:
          case "end":
            return _context.stop();
        }
      }
    },
    _marked);
}
let hG = helloWorldGenerator();
console.log(hG.next());
console.log(hG.next());
console.log(hG.next());